<?php
/**
 * 蜗牛博客
 * 本文件内代码属于“蜗牛博客”项目，禁止修改再发布本程序源码，禁止去除页面底部的版权申明。所有版权保留
 * User: upliu
 * Email: 753073861@qq.com
 * QQ: 753073861
 * 程序主页：http://demo.upliu.net/snail-blog/
 */

define('_CSRF_MASK_LENGTH', 8);

function it_must_post()
{
    if (!IS_POST) {
        http_bad_request('Only POST allowed');
    }
}

function http_bad_request($msg = '')
{
    header("HTTP/1.1 400 Bad Request");
    if ($msg) {
        snail_alert($msg);
    } else {
        exit();
    }
}

/**
 * 输出提示信息
 * @param $message
 * @param string $url
 */
function snail_alert($message, $url = '')
{
    $a = $url ?
        "<a href='$url'>点击跳转到:$url</a>"
        :
        "<a href='javascript:history.back();'>点击返回</a>"
        ;
    echo <<<DRT
<head>
<meta charset="utf-8">
<title>$message</title>
<head>
<body>
    <div style="text-align: center">
        <p>$message</p>
        <p>
            $a
        </p>
    </div>
</body>
DRT;

    exit();
}

/**
 * @property string $href
 * @property bool $isActive
 * 一个 Nav 实例代表一个导航上面的菜单链接
 * Class Nav
 */
class Nav extends BaseObject
{
    protected $_href = '';
    public $label = '';
    protected $_isActive = null;
    public $target = '_self';
    public $type = self::TYPE_NAV;
    public $subNavs = array();
    public $highlightHrefs = array();
    public $show = true;
    
    const TYPE_NAV = 'nav';
    const TYPE_HREF = 'href';

    public function init()
    {
        parent::init();
        $this->highlightHrefs[] = $this->href;
    }

    public function setHref($href)
    {
        $this->_href = $href;
    }

    public function getHref()
    {
        if (!$this->_href) {
            foreach ($this->subNavs as $nav) {
                if ($href = $nav->href) {
                    $this->_href = $href;
                    break;
                }
            }
        }

        return $this->_href;
    }

    public function getIsActive()
    {
        if (null === $this->_isActive) {
            foreach ($this->subNavs as $nav) {
                if ($nav->isActive) {
                    $this->_isActive = true;
                    break;
                }
            }

            if (!$this->_isActive) {
                if (in_array(pathinfo(get_script_url(), PATHINFO_BASENAME), $this->highlightHrefs)) {
                    $this->_isActive = true;
                } else {
                    $this->_isActive = false;
                }
            }
        }

        return $this->_isActive;
    }

    public function setIsActive($isActive)
    {
        $this->_isActive = $isActive;
    }

    public function render()
    {
        $nav = $this;
        if (!$this->show) {
            return ;
        }
        ?>
        <li class="<?php if ($nav->isActive) echo 'active'; ?>"><a href="<?php e($nav->href); ?>" target="<?php e($nav->target); ?>"><?php e($nav->label); ?></a></li>
<?php
    }
}

class BaseObject
{
    public function __construct(array $config = array())
    {
        foreach ($config as $name => $value) {
            $this->$name = $value;
        }
        $this->init();
    }
    
    public function init()
    {
    }

    public function __get($name)
    {
        $getter = 'get' . $name;
        if (method_exists($this, $getter)) {
            return $this->$getter();
        } elseif (method_exists($this, 'set' . $name)) {
            throw new RuntimeException('Getting write-only property: ' . get_class($this) . '::' . $name);
        } else {
            throw new RuntimeException('Getting unknown property: ' . get_class($this) . '::' . $name);
        }
    }

    public function __set($name, $value)
    {
        $setter = 'set' . $name;
        if (method_exists($this, $setter)) {
            $this->$setter($value);
        } elseif (method_exists($this, 'get' . $name)) {
            throw new RuntimeException('Setting read-only property: ' . get_class($this) . '::' . $name);
        } else {
            throw new RuntimeException('Setting unknown property: ' . get_class($this) . '::' . $name);
        }
    }
}

/**
 * @param null $key
 * @param string $default
 * @return string
 */
function get($key = null, $default = '')
{
    if (null === $key) {
        return $_GET;
    }
    if (isset($_GET[$key])) {
        return $_GET[$key];
    }

    return $default;
}

function post($key = null, $default = '')
{
    if (null === $key) {
        return $_POST;
    }
    if (isset($_POST[$key])) {
        return trim($_POST[$key]);
    }

    return $default;
}

function echo_json_success($message = '', $data = array(), $redirect = '')
{
    $message = $message ?: '操作成功';
    if ($redirect) {
        set_success_message($message);
        $message = '';
    }
    exit( json_encode(array(
        'message' => $message,
        'code' => 0,
        'data' => $data,
        'redirect' => $redirect,
    )) );
}

function echo_json_error($message = '', $code = 1, $data = array(), $redirect = '')
{
    exit ( json_encode(array(
        'message' => $message ?: '操作失败',
        'code' => $code,
        'data' => $data,
        'redirect' => $redirect,
    )) );
}

function csrf_token_hidden_input_html()
{
    return '<input type="hidden" name="_csrf" value="'.csrf_token_get().'">';
}

function csrf_token_get()
{
    $_SESSION['_csrf'] = $trueToken = !empty($_SESSION['_csrf']) ? $_SESSION['_csrf'] : md5(uniqid());

    $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.';
    $mask = substr(str_shuffle(str_repeat($chars, 5)), 0, _CSRF_MASK_LENGTH);
    $csrfToken = str_replace('+', '.', base64_encode($mask . _xor_tokens($trueToken, $mask)));
    
    return $csrfToken;
}

function csrf_token_is_valid()
{
    static $pass;
    if (null === $pass) {
        $input = !empty($_POST['_csrf']) ? $_POST['_csrf'] : '';
        if ($input && _validate_csrf_token($input)) {
            $pass = true;
        } else {
            $pass = false;
        }
    }
    return $pass;
}

function _xor_tokens($token1, $token2)
{
    $n1 = strlen($token1);
    $n2 = strlen($token2);
    if ($n1 > $n2) {
        $token2 = str_pad($token2, $n1, $token2);
    } elseif ($n1 < $n2) {
        $token1 = str_pad($token1, $n2, $n1 === 0 ? ' ' : $token1);
    }

    return $token1 ^ $token2;
}

function _validate_csrf_token($token)
{
    if (empty($_SESSION['_csrf'])) {
        return false;
    }
    $trueToken = $_SESSION['_csrf'];

    $token = base64_decode(str_replace('.', '+', $token));
    $n = strlen($token);
    if ($n <= _CSRF_MASK_LENGTH) {
        return false;
    }
    $mask = substr($token, 0, _CSRF_MASK_LENGTH);
    $token = substr($token, _CSRF_MASK_LENGTH, $n - _CSRF_MASK_LENGTH);
    $token = _xor_tokens($mask, $token);
    
    return $token === $trueToken;
}

function set_success_message($msg)
{
    $_SESSION['_message'] = array($msg, 'success');
}

function set_error_message($msg)
{
    $_SESSION['_message'] = array($msg, 'danger');
}

function get_message($keep = false)
{
    if (empty($_SESSION['_message'])) {
        return false;
    }

    $msg = $_SESSION['_message'];
    if (!$keep) {
        unset($_SESSION['_message']);
    }
    return $msg;
}

function html_select_render($values, $prompt = '', $config = array())
{
    $config += array(
        'name' => '',
        'class' => 'form-control',
        'selected' => '',
    );
    if (empty($config['id'])) {
        $config['id'] = $config['name'];
    }
    $os = array();
    if ($prompt) {
        $os[] = "<option value=''>$prompt</option>";
    }
    foreach ((array)$values as $k => $v) {
        $os[] = "<option" . ($config['selected'] !== '' && $config['selected'] !== null && $k == $config['selected'] ? ' selected' : '') . " value='".htmlspecialchars($k)."'>".str_replace('!BLANK!', '&nbsp;', htmlspecialchars(str_replace('&nbsp;', '!BLANK!', $v)))."</option>";
    }

    return "<select id='{$config['id']}' name='{$config['name']}' class='{$config['class']}'>" . implode("\n", $os) . "</select>";
}

function e_if_true($var, $format)
{
    if ($var) {
        e(sprintf($format, $var));
    }
}

function e($str)
{
    echo hsc($str);
}

function hsc($str)
{
    return htmlspecialchars($str, ENT_COMPAT | ENT_HTML401, 'UTF-8');
}

function echo_active_if_true($link)
{
    if (is_active_menu($link)) {
        echo 'active';
    }
}

function is_active_menu($link)
{
    if (stripos(get_script_url(), $link) !== false) {
        return true;
    }

    return false;
}

/**
 * 跳转到后台首页
 */
function redirect_admin()
{
    $url = get_admin_home_path();
    redirect_path($url);
}

/**
 * 跳转到前台首页
 */
function redirect_home()
{
    redirect_path(get_project_url());
}

function get_admin_home_path()
{
    return get_url('/admin');
}

function get_url($path)
{
    return get_project_url() . '/' . ltrim($path, '/');
}

function redirect_path($path, $success_message = '', $error_message = '')
{
    if ($success_message) {
        set_success_message($success_message);
    } else if ($error_message) {
        set_error_message($error_message);
    }
    if ($path == '') {
        $path = '/';
    }
    header('Location: ' . $path);
    exit();
}

function assets_url($path)
{
    echo get_url('/assets/' . ltrim($path, '/'));
}

function admin_assets_url($path)
{
    echo get_admin_home_path() . '/assets/' . ltrim($path, '/');
}

function get_project_url()
{
    static $project_path;
    if ($project_path === null) {
        $project_path = preg_replace('#(admin|imageuploader)$#', '', get_base_url());
        $project_path = rtrim($project_path, '/');
    }

    return $project_path;
}

function renderWithLayout($__view_name, $__data = array())
{
    $conent = render($__view_name, $__data);
    return render('layout', $__data + array(
        '__content' => $conent,
    ));
}

/**
 * 渲染视图
 * @param $__view_name
 * @param array $__data
 * @param bool|true $echo
 * @return bool
 */
function render($__view_name, $__data = array())
{
    extract($__data, EXTR_SKIP);
    $__dir = getcwd();
    chdir(DIR_VIEWS);
    ob_start();
    $old_levle = error_reporting();
    // 禁掉 E_NOTICE 级别错误，减少视图里面的一些判断
    error_reporting($old_levle & ~E_NOTICE);
    include DIR_VIEWS . '/' . rtrim($__view_name, '.php') . '.php';
    error_reporting($old_levle);
    $buff = ob_get_clean();
    chdir($__dir);

    return $buff;
}

/**
 * @param string $url
 * @param int $total_count 总数
 * @param int $page_size 每页数
 * @param int $current_page 当前页
 * @param string $page_param $_GET 里面标识页码的变量
 * @param int $link_num 生成的链接数量
 * @return string
 */
function pagination($url, $total_count, $page_size, $current_page = 1, $page_param = '_page', $link_num = 10)
{
    if (empty($url)) {
        $url = $_SERVER['REQUEST_URI'];
    }
    if ($current_page < 1) {
        $current_page = get($page_param, 1);
    }

    $url = rtrim($url, '?');
    $last_page = ceil($total_count / $page_size);
    $first_page = 1;
    // 一共只有 1 页情况，我们不显示分页按钮
    if ($last_page < 2) {
        return '';
    }

    $href_elements = array();
    if ($current_page == $first_page) {
        $href_elements[] = "<li class='disabled first'><span>首页</span></li>";
        $href_elements[] = "<li class='disabled prev'><span>上一页</span></li>";
    } else {
        $first_href = add_query_to_url($url, $page_param);
        $prev_href = add_query_to_url($url, $page_param, $current_page - 1);
        $href_elements[] = "<li class='first'><a href='$first_href'>首页</a></li>";
        $href_elements[] = "<li class='prev'><a href='$prev_href'>上一页</a></li>";
    }

    $begin_page = max(1, $current_page - ceil($link_num / 2));
    if (($end_page = $begin_page + $link_num - 1) > $last_page) {
        $end_page = $last_page;
        $begin_page = max(1, $end_page - $link_num + 1);
    }
    for ($i = $begin_page; $i <= $end_page; $i++) {
        if ($i == $current_page) {
            $href_elements[] = "<li class='active'><span>$i</span></li>";
        } else {
            $href_elements[] = "<li><a href='" . add_query_to_url($url, $page_param, $i) . "'>$i</a></li>";
        }
    }

    if ($current_page == $last_page) {
        $href_elements[] = "<li class='disabled next'><span>下一页</span></li>";
        $href_elements[] = "<li class='disabled last'><span>末页</span></li>";
    } else {
        $last_href = add_query_to_url($url, $page_param, $last_page);
        $next_href = add_query_to_url($url, $page_param, $current_page + 1);
        $href_elements[] = "<li class='next'><a href='$next_href'>下一页</a></li>";
        $href_elements[] = "<li class='last'><a href='$last_href'>末页</a></li>";
    }

    return "<div class='pagination-wrap'><ul class='pagination'>\n" . implode("\n", $href_elements) . "\n</ul></div>";
}

function get_correct_page($page, $total_count, $page_size)
{
    $last_page = ceil($total_count / $page_size);
    if ($page < 1) return 1;
    if ($page > $last_page) return $last_page;

    return $page;
}

/**
 * 检查访问的页码是否超出总数
 * @param $page
 * @param $total_count
 * @param $page_size
 * @param bool|true $throw_exception
 * @return bool
 * @throws Exception
 */
function check_is_correct_page($page, $total_count, $page_size, $throw_exception = true)
{
    $last_page = ceil($total_count / $page_size);
    if ($page < 1 || ($last_page > 0 && $page > $last_page)) {
        if ($throw_exception) {
            throw new Exception('The page you are looking for is not found.', 404);
        }

        return false;
    }

    return true;
}

function add_query_to_url($url, $param, $value = null)
{
    $parsed = parse_url($url);
    if (empty($parsed['query'])) {
        if ($value !== null) {
            return $url . '?' . http_build_query(array($param => $value));
        } else {
            return rtrim($url, '?');
        }
    }

    $params = array();
    parse_str($parsed['query'], $params);
    if ($value !== null) {
        $params[$param] = $value;
    } else {
        unset($params[$param]);
    }

    $scheme = isset($parsed['scheme']) ? $parsed['scheme'] . '://' : '';
    $host = isset($parsed['host']) ? $parsed['host'] : '';
    $port = isset($parsed['port']) ? ':' . $parsed['port'] : '';
    $user = isset($parsed['user']) ? $parsed['user'] : '';
    $pass = isset($parsed['pass']) ? ':' . $parsed['pass'] : '';
    $pass = ($user || $pass) ? "$pass@" : '';
    $path = isset($parsed['path']) ? $parsed['path'] : '';
    $query = !empty($params) ? ('?'.http_build_query($params)) : '';
    $fragment = isset($parsed['fragment']) ? '#' . $parsed['fragment'] : '';
    return "$scheme$user$pass$host$port$path$query$fragment";
}

/**
 * 该函数参考了 yii2 框架的相关代码
 * @return string
 * @see \yii\web\Request::getMethod()
 */
function get_method()
{
    if (isset($_POST['_method'])) {
        return strtoupper($_POST['_method']);
    } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
        return strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
    } else {
        return isset($_SERVER['REQUEST_METHOD']) ? strtoupper($_SERVER['REQUEST_METHOD']) : 'GET';
    }
}

/**
 * 该函数参考了 yii2 框架的相关代码
 * @return mixed|string
 * @throws Exception
 * @see \yii\web\Request::getBaseUrl()
 */
function get_base_url()
{
    static $baseUrl;
    if ($baseUrl === null) {
        $baseUrl = rtrim(dirname(get_script_url()), '\\/');
    }

    return $baseUrl;
}

/**
 * 该函数参考了 yii2 框架的相关代码
 * @return mixed|string
 * @throws Exception
 * @see \yii\web\Request::getHostInfo()
 */
function get_host_info()
{
    static $host_info;
    if (!$host_info) {
        $secure = isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') === 0 || $_SERVER['HTTPS'] == 1)
            || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0;

        $port = isset($_SERVER['SERVER_PORT'])
            ? (int)$_SERVER['SERVER_PORT']
            : ($secure ? 443 : 80);

        $http = $secure ? 'https' : 'http';
        if (isset($_SERVER['HTTP_HOST'])) {
            $host_info = $http . '://' . $_SERVER['HTTP_HOST'];
        } else {
            $host_info = $http . '://' . $_SERVER['SERVER_NAME'];
            if (($port !== 80 && !$secure) || ($port !== 443 && $secure)) {
                $host_info .= ':' . $port;
            }
        }
    }

    return $host_info;
}

/**
 * 该函数参考了 yii2 框架的相关代码
 * @return mixed|string
 * @throws Exception
 * @see \yii\web\Request::getScriptUrl()
 */
function get_script_url()
{
    static $scriptUrl;
    if ($scriptUrl === null) {
        $scriptFile = $_SERVER['SCRIPT_FILENAME'];
        $scriptName = basename($scriptFile);
        if (basename($_SERVER['SCRIPT_NAME']) === $scriptName) {
            $scriptUrl = $_SERVER['SCRIPT_NAME'];
        } elseif (basename($_SERVER['PHP_SELF']) === $scriptName) {
            $scriptUrl = $_SERVER['PHP_SELF'];
        } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $scriptName) {
            $scriptUrl = $_SERVER['ORIG_SCRIPT_NAME'];
        } elseif (($pos = strpos($_SERVER['PHP_SELF'], '/' . $scriptName)) !== false) {
            $scriptUrl = substr($_SERVER['SCRIPT_NAME'], 0, $pos) . '/' . $scriptName;
        } elseif (!empty($_SERVER['DOCUMENT_ROOT']) && strpos($scriptFile, $_SERVER['DOCUMENT_ROOT']) === 0) {
            $scriptUrl = str_replace('\\', '/', str_replace($_SERVER['DOCUMENT_ROOT'], '', $scriptFile));
        } else {
            throw new Exception('Unable to determine the entry script URL.', 500);
        }
    }

    return $scriptUrl;
}

function get_client_ip()
{
    return $_SERVER['REMOTE_ADDR'];
}

/**
 * array_column 这个函数从 PHP5.5.0 开始自带
 * 模仿该函数功能使得低版本 PHP 也可以使用该函数
 */
if (!function_exists('array_column')) {
    function array_column(array $array, $column, $index_key = null)
    {
        $result = array();
        foreach ((array)$array as $item) {
            if ($index_key !== null) {
                $result[$item[$index_key]] = $item[$column];
            } else {
                $result[] = $item[$column];
            }
        }

        return $result;
    }
}

function array_index($input, $key, $limit = false)
{
    $input = (array)$input;
    $result = array();
    foreach ($input as $item) {
        if ($limit) {
            $result[$item[$key]] = $item;
        } else {
            $result[$item[$key]][] = $item;
        }
    }

    return $result;
}

function array_remove_interger_key(&$array)
{
    foreach ($array as $k => $v) {
        if (is_integer($k)) {
            unset($array[$k]);
        }
    }
}

class Db
{

    public $dsn;
    public $username;
    public $password;

    public $lastRunStatement;

    /**
     * @param array $config
     * @return static
     */
    public static function getInstance($config = array())
    {
        if (static::$_instance === null) {
            if (empty($config)) {
                $config = include __DIR__ . '/../config.php';
                $config = array_merge(array(
                    'host' => 'localhost',
                    'port' => 3306,
                    'password' => '',
                ), $config);
            }
            
            static::$_instance = new static($config['host'], $config['dbname'], $config['username'], $config['password'], $config['port']);
        }
        
        return static::$_instance;
    }

    protected static $_instance;
    protected function __construct($host, $dbname, $username, $password = '', $port = 3306)
    {
        $this->dsn = "mysql:host=$host;port=$port;dbname=$dbname;charset=utf8";
        $this->username = $username;
        $this->password = $password;
    }

    protected $_pdo;
    public function getConnection()
    {
        if ($this->_pdo === null) {
            $options = array(
                PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
            );
            $this->_pdo = new PDO($this->dsn, $this->username, $this->password, $options);
            $this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }

        return $this->_pdo;
    }

    /**
     * @param array|string $statement
     * @param array $params
     * @return PDOStatement
     * @throws Exception
     */
    protected function doExecute($statement, $params = array())
    {
        if (!is_array($params)) {
            $params = (array)$params;
        }
        if (is_array($statement)) {
            list($statement, $where) = $statement;
            list($_statement, $_params) = $this->andWhere($where);
            $statement .= $_statement;
            $params = array_merge($params, $_params);
        }

        $this->lastRunStatement = $statement;
        file_put_contents('/tmp/snail-blog.sql', $statement."\n", FILE_APPEND);
        $stmt = $this->getConnection()->prepare($statement);
        file_put_contents('/tmp/snail-blog.sql', implode(',', $params)."\n\n", FILE_APPEND);
        try {
            $stmt->execute($params);
        } catch (Exception $e) {
            echo $statement, implode(',', $params), "\n";
            throw $e;
        }
        return $stmt;
    }

    public function lastInsertId()
    {
        return $this->getConnection()->lastInsertId();
    }

    public function update($table, array $data, $where = '', $params = array())
    {
        $statement = "update `$table` set ";
        $columns = array();
        $values = array();
        foreach ($data as $column => $value) {
            $columns[] = "`$column`=?";
            $values[] = $value;
        }
        $statement .= implode(',', $columns);
        if (!empty($where)) {
            $statement .= " where $where";
        }
        $params = (array)$params;
        if (!empty($params)) {
            $values = array_merge($values, $params);
        }
        return $this->execute($statement, $values);
    }

    public function insert($table, array $data)
    {
        $statement = "insert `$table` set ";
        $columns = array();
        $values = array();
        foreach ($data as $column => $value) {
            $columns[] = "`$column`=?";
            $values[] = $value;
        }
        $statement .= implode(',', $columns);
        $this->execute($statement, $values);

        return $this->lastInsertId();
    }

    public function delete($table, $where, $params = array())
    {
        $statement = "delete from `$table` where $where";
        return $this->execute($statement, $params);
    }

    public function execute($statement, $params = array())
    {
        return $this->doExecute($statement, $params)->rowCount();
    }

    public function query($statement, $params = array(), $fetch_style = PDO::FETCH_ASSOC)
    {
        return $this->doExecute($statement, $params)->fetch($fetch_style);
    }

    public function queryColumn($statement, $params = array())
    {
        return $this->doExecute($statement, $params)->fetchColumn(0);
    }

    public function queryAll($statement, $params = array(), $fetch_style = PDO::FETCH_ASSOC)
    {
        return $this->doExecute($statement, $params)->fetchAll($fetch_style);
    }

    /**
     * @param $statement
     * @param $column
     * @param $values
     * @param array $params
     * @return array
     */
    public function queryAllWhereIn($statement, $column, $values, $params = array())
    {
        if (empty($values)) {
            return array();
        }
        if (stripos($statement, 'where') === false) {
            $statement .= ' where 1=1 ';
        }
        $statement .= " and `$column` in (" .
            rtrim(str_repeat("?,", count($values)), ',')
            . ")";
        $params = array_merge($values, $params);
        return $this->doExecute($statement, $params)->fetchAll();
    }

    protected function andWhere($where = array())
    {
        $statement = ' where 1=1 ';
        $params = array();
        $tails = array();
        foreach ($where as $column => $value) {
            if ($value === '') {
                continue;
            }
            if (is_integer($column)) {
                $tails[] = $value;
                continue;
            }
            $u = explode(' ', $column);
            if (count($u) == 1) {
                $column .= ' =';
            } else {
                if (strcasecmp($u[1], 'like') == 0) {
                    $value = '%' . str_replace('%', '\%', $value) . '%';
                }
            }
            $statement .= " and $column ? ";
            $params[] = $value;
        }

        return array($statement . implode(' ', $tails), $params);
    }

    /**
     * @param string $table
     * @param null|array $where
     * @param array $params
     * @return string
     */
    public function count($table, $where, $params = array())
    {
        if (is_array($where)) {
            array_remove_interger_key($where);
            return $this->queryColumn(array("select count(1) from $table", $where), $params);
        } else {
            return $this->queryColumn("select count(1) from $table", $params);
        }
    }

    public function getList($table, $where = array(), $page_size = 15)
    {
        $count = $this->count($table, $where);
        $offset = (get('_page', 1) - 1) * $page_size;
        $pagination = pagination('', $count, $page_size, '');
        $where[] = "limit $offset,$page_size";

        $items = $this->queryAll(array("select * from $table", $where));

        return array(
            'items' => $items,
            'pagination' => array(
                'html' => $pagination,
                'count' => $count
            ),
        );
    }
}
